package com.stud.system.service.impl;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.stud.common.enums.BusinessEnum;
import com.stud.common.exception.ServiceException;
import com.stud.common.mybatis.BaseServiceImpl;
import com.stud.common.utils.file.FileUtils;
import com.stud.system.domain.FileLink;
import com.stud.system.factory.FileEngineBuilder;
import com.stud.system.mapper.FileLinkMapper;
import com.stud.system.properties.FileProperties;
import com.stud.system.service.FileEngine;
import com.stud.system.service.FileLinkService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;


/**
 * 系统文件关联Service业务层处理
 *
 * @author Stud
 * @date 2022-10-08
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileLinkServiceImpl extends BaseServiceImpl<FileLinkMapper, FileLink> implements FileLinkService {

    private final FileProperties fileProperties;

    private final FileEngine fileEngine = FileEngineBuilder.build();

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileLink upload(BusinessEnum businessEnum, MultipartFile file) {
        return uploadLink(businessEnum, null, file);
    }

    @Override
    public FileLink link(Long id, Long businessId) {
        LambdaUpdateWrapper<FileLink> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(FileLink::getId, id);
        updateWrapper.set(FileLink::getBusinessId, businessId);
        this.update(updateWrapper);

        return this.getById(id);
    }

    @Override
    public List<FileLink> linkBatch(Long[] ids, Long businessId) {
        LambdaUpdateWrapper<FileLink> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.in(FileLink::getId, ids);
        updateWrapper.set(FileLink::getBusinessId, businessId);
        this.update(updateWrapper);

        return this.listByIds(Arrays.asList(ids));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileLink uploadLink(BusinessEnum businessEnum, Long businessId, MultipartFile file) {
        //如果是单文件,则先删除之前已绑定的文件关系
        if (!businessEnum.isMulti()) {
            LambdaQueryWrapper<FileLink> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(FileLink::getBusinessEnum, businessEnum.getCode());
            queryWrapper.eq(FileLink::getBusinessId, businessId);
            this.remove(queryWrapper);
        }
        String fileName = file.getOriginalFilename();

        int pointIndex = fileName.lastIndexOf(".");
        String fileAss = fileName.substring(pointIndex);

        String fileKey = fileEngine.upload(file, businessEnum.getCode(), UUID.randomUUID() + fileAss);

        if (StringUtils.isBlank(fileKey)) {
            throw new ServiceException("上传失败");
        }

        FileLink fileLink = new FileLink();
        fileLink.setFileName(fileName);
        fileLink.setFileAss(fileAss);
        fileLink.setFileType(FileUtils.getFileTypeByAss(fileAss));
        fileLink.setFileSize(file.getSize());
        fileLink.setFileKey(fileKey);
        fileLink.setBusinessEnum(businessEnum.getCode());
        fileLink.setBusinessId(businessId);
        boolean result = this.save(fileLink);

        if (!result) {
            throw new ServiceException("文件关联失败");
        }

        return fileLink;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<FileLink> uploadLink(BusinessEnum businessEnum, Long businessId, MultipartFile[] files) {
        //如果是单文件,则显出之前已绑定的文件关系
        if (!businessEnum.isMulti()) {
            throw new ServiceException("当前业务不支持多文件上传");
        }
        List<FileLink> fileLinks = Lists.newCopyOnWriteArrayList();
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();

            int pointIndex = fileName.lastIndexOf(".");
            String fileAss = fileName.substring(pointIndex);

            String fileKey = fileEngine.upload(file, businessEnum.getCode(), UUID.randomUUID() + fileAss);

            if (StringUtils.isBlank(fileKey)) {
                throw new ServiceException("上传失败");
            }

            FileLink fileLink = new FileLink();
            fileLink.setFileName(fileName);
            fileLink.setFileAss(fileAss);
            fileLink.setFileType(FileUtils.getFileTypeByAss(fileAss));
            fileLink.setFileSize(file.getSize());
            fileLink.setFileKey(fileKey);
            fileLink.setBusinessEnum(businessEnum.getCode());
            fileLink.setBusinessId(businessId);
            boolean result = this.save(fileLink);
            if (!result) {
                throw new ServiceException("文件关联失败");
            }
            fileLinks.add(fileLink);
        }
        return fileLinks;
    }

    @Override
    public Map<Long, List<FileLink>> linkMap(BusinessEnum businessEnum, List<Long> businessIds) {
        if (CollectionUtils.isEmpty(businessIds)) {
            return Maps.newHashMap();
        }
        LambdaQueryWrapper<FileLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FileLink::getBusinessEnum, businessEnum);
        queryWrapper.in(FileLink::getBusinessId, businessIds);
        List<FileLink> list = this.list(queryWrapper);

        Map<Long, List<FileLink>> fileLinkMap = Maps.newHashMap();

        list.forEach(fileLink -> {
            List<FileLink> tempList;
            if (fileLinkMap.containsKey(fileLink.getBusinessId())) {
                tempList = fileLinkMap.get(fileLink.getBusinessId());
            } else {
                tempList = Lists.newArrayList();
                fileLinkMap.put(fileLink.getBusinessId(), tempList);
            }
            tempList.add(fileLink);
        });
        return fileLinkMap;
    }

    @Override
    public Map<Long, List<Long>> linkIdMap(BusinessEnum businessEnum, List<Long> businessIds) {
        if (CollectionUtils.isEmpty(businessIds)) {
            return Maps.newHashMap();
        }
        LambdaQueryWrapper<FileLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(FileLink::getId, FileLink::getBusinessId);
        queryWrapper.eq(FileLink::getBusinessEnum, businessEnum);
        queryWrapper.in(FileLink::getBusinessId, businessIds);
        List<FileLink> list = this.list(queryWrapper);

        Map<Long, List<Long>> fileLinkMap = Maps.newHashMap();

        list.forEach(fileLink -> {
            List<Long> tempList;
            if (fileLinkMap.containsKey(fileLink.getBusinessId())) {
                tempList = fileLinkMap.get(fileLink.getBusinessId());
            } else {
                tempList = Lists.newArrayList();
                fileLinkMap.put(fileLink.getBusinessId(), tempList);
            }
            tempList.add(fileLink.getId());
        });
        return fileLinkMap;
    }

    /**
     * 这里不加事务,防止出现数据问滚,但是文件不回滚.这样批量删除效率略高
     * 如果必须要求精确,后续可以写成一个一个删
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deletePhysical(Long[] ids) {
        List<FileLink> list = this.listByIds(Arrays.asList(ids));
        return deletePhysical(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deletePhysical(List<FileLink> list) {

        if (CollectionUtils.isEmpty(list)) {
            throw new ServiceException("数据异常");
        }

        List<String> fileKeys = list.stream().map(FileLink::getFileKey).collect(Collectors.toList());
        fileEngine.remove(fileKeys);

        return this.removeByIds(list);
    }

    @Override
    public boolean fileReport(String fileName, MultipartFile file) {
        try (InputStream inputStream = file.getInputStream()) {
            String folder = fileProperties.getFolder() + "/sftp";
            log.info("接收文件:{},存放地址父目录:{}", fileName, folder);
            FileUtil.writeFromStream(inputStream, folder + "/" + fileName);
            return true;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

    @Override
    public FileLink getLinkByFileKey(String fileKey) {
        LambdaQueryWrapper<FileLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FileLink::getFileKey, fileKey);
        List<FileLink> list = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        } else {
            return list.get(0);
        }
    }

    @Override
    public String getDownloadUrl(String fileKey) {
        return fileEngine.getDownloadUrl(fileKey);
    }

}
